package com.dxy.shardingjdbc.controller;


import cn.hutool.core.lang.Assert;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dxy.shardingjdbc.model.po.Order;
import com.dxy.shardingjdbc.service.OrderService;
import com.dxy.shardingjdbc.utils.IdUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * 订单表(Order)表控制层
 *
 * @author dangxianyue
 * @since 2023-02-02 21:10:17
 */
@Api(tags = "订单接口")
@RestController
@RequestMapping("order")
public class OrderController {

    private static Long[] userIdArr = {1000231L, 1000232L, 1000233L, 1000234L, 1000235L, 1000236L};
    private static Integer[] orderStatusArr = {0, 1, 2, 3, 4};
    private static String[] bizCodeArr = {"SA", "SB", "SC", "SD"};
    private Random random = new Random();

    @Resource
    private OrderService orderService;

    @ApiOperation("创建100条订单")
    @GetMapping("/create100Order")
    public String create100Order() {
        for (int i = 1; i <= 100; i++) {
            Long userId = userIdArr[random.nextInt(6)];
            Integer orderStatus = orderStatusArr[random.nextInt(5)];
            String bizCode = bizCodeArr[random.nextInt(4)];
            Order order = new Order()
                    .setOrderSn(IdUtil.getOrderCode(bizCode, userId))
                    .setUserId(userId)
                    .setOrderName("订单 " + i)
                    .setOrderStatus(orderStatus);
            orderService.save(order);
            System.out.println("订单创建成功：" + JSON.toJSONString(order));
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return "OK";
    }

    @ApiOperation("新增订单")
    @PostMapping("/save")
    public Order save(Long userId, String orderName, Integer orderStatus) {
        Assert.notNull(userId);
        Assert.notBlank(orderName);
        Assert.notNull(orderStatus);
        Order order = new Order()
                .setUserId(userId)
                .setOrderSn(IdUtil.getOrderCode(bizCodeArr[random.nextInt(4)], userId))
                .setOrderName(orderName)
                .setOrderStatus(orderStatus);
        orderService.save(order);
        return order;
    }

    /*
     * 列表查询时，若不同过分片键匹配，则会先去各个节点（t_order_0、t_order_1、t_order_2）查回内存中进行汇总（相当于不分表了）
     */
    @ApiOperation("订单列表")
    @GetMapping("/list")
    public List<Order> list() {
        return orderService.list();
    }

    /**
     * 根据 id 查找
     * id不是分片键，则会去各个节点查找后再执行 union all 操作，所以一般不会根据id查找，而是根据 order_sn
     * id 是数据库的主键，无业务意义，InnoDB 引擎规定，数据存在叶子节点上，根据 id 进行排列，故要求是大致递增的
     * 此处主键id的作用的，用雪花算法id保持大致有序，提高插入效率
     */
    @ApiOperation("根据id查找")
    @GetMapping("/findById/{id}")
    public Order findById(@PathVariable("id") Long id) {
        return orderService.getById(id);
    }


    /**
     * 根据 order_sn 查找
     * orderSn 是订单编号，跟支付流水号一样，是具有业务意义的唯一性标识，根据特定的业务规则生成
     * 由于 order_sn 是分片键，会直接找到数据所在的表
     */
    @ApiOperation("根据orderSn查找")
    @GetMapping("/findByOrderSn/{orderSn}")
    public Order findByOrderSn(@PathVariable("orderSn") String orderSn) {
        return orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderSn, orderSn));
    }

    @ApiOperation("根据userId查找")
    @GetMapping("/findByUserId")
    public List<Order> findByUserId(Long userId) {
        return orderService.list(new LambdaQueryWrapper<Order>().eq(Order::getUserId, userId));
    }

    @ApiOperation("根据orderSn和userId查找")
    @GetMapping("/findByOrderSnAndUserId")
    public Order findByOrderSnAndUserId(String orderSn, Long userId) {
        return orderService.getOne(new LambdaQueryWrapper<Order>()
                .eq(Order::getOrderSn, orderSn)
                .eq(Order::getUserId, userId));
    }


    /**
     * 先去所有的分片节点查到内存中再做汇总合并（相当于不分表了，效率极低）
     */
    @ApiOperation("根据orderStatus进行统计")
    @GetMapping("/statByOrderStatus")
    public Map<String, Object> statByOrderStatus() {
        LambdaQueryWrapper<Order> wrapper = new QueryWrapper<Order>()
                .select("order_status,count(*) as order_num")
                .lambda()
                .groupBy(Order::getOrderStatus);
        List<Map<String, Object>> list = orderService.listMaps(wrapper);

        // list to map
        Map<String, Object> map = new HashMap<>();
        list.forEach(o -> map.put(o.get("order_status").toString(), o.get("order_num")));
        return map;
    }

}

